/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 - 2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/

package com.genia.toolbox.web.servlet;

import java.util.Enumeration;
import java.util.Properties;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import com.genia.toolbox.web.io.SubServletHttpServletRequest;

/**
 * a Servlet Wrapping Controller that can wrap a Spring bean.
 */
public class CustomServletWrappingController
    extends AbstractController
    implements BeanNameAware, InitializingBean, DisposableBean
{
  /**
   * the {@link Servlet} to wrapp.
   */
  private Servlet servletInstance;

  /**
   * the name of the servlet.
   */
  private String servletName;

  /**
   * the path of the servlet.
   */
  private String servletPath;
  
  /**
   * the initialization parameters of the servlet.
   */
  private Properties initParameters = new Properties();

  /**
   * the name of the bean in the spring container.
   */
  private String beanName;



  /**
   * Initialize the wrapped Servlet instance.
   * 
   * @throws Exception
   *           if an error occured
   * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
   */
  public void afterPropertiesSet()
      throws Exception
  {
    if (this.servletName == null) {
      this.servletName = this.beanName;
    }
    this.servletInstance.init(new DelegatingServletConfig());
  }



  /**
   * Invoke the the wrapped Servlet instance.
   * 
   * @param request
   *          the http request
   * @param response
   *          the http response
   * @return <code>null</code>
   * @throws Exception
   *           if an error occured
   * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest,
   *      javax.servlet.ServletResponse)
   */
  @Override
  protected ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response)
      throws Exception
  {
    
    HttpServletRequest newRequest = request;
    if(getServletPath() != null) {
      newRequest = new SubServletHttpServletRequest(request, getServletPath());
    }
    this.servletInstance.service(newRequest, response);
    return null;
  }



  /**
   * Destroy the wrapped Servlet instance.
   * 
   * @see javax.servlet.Servlet#destroy()
   */
  public void destroy()
  {
    this.servletInstance.destroy();
  }

  /**
   * Internal implementation of the ServletConfig interface, to be passed to the
   * wrapped servlet. Delegates to ServletWrappingController fields and methods
   * to provide init parameters and other environment info.
   */
  private class DelegatingServletConfig
      implements ServletConfig
  {

    /**
     * returns the name of the servlet.
     * 
     * @return the name of the servlet
     */
    public String getServletName()
    {
      return servletName;
    }



    /**
     * returns the {@link ServletContext} of the servlet.
     * 
     * @return the {@link ServletContext} of the servlet
     */
    public ServletContext getServletContext()
    {
      return CustomServletWrappingController.this.getServletContext();
    }



    /**
     * returns the init parameter associated to the given name.
     * 
     * @param paramName
     *          the name of the parameter
     * @return the init parameter associated to the given name
     */
    public String getInitParameter(String paramName)
    {
      return initParameters.getProperty(paramName);
    }



    /**
     * returns an {@link Enumeration} over the names of the parameters.
     * 
     * @return an {@link Enumeration} over the names of the parameters
     */
    public Enumeration<?> getInitParameterNames()
    {
      return initParameters.keys();
    }
  }



  /**
   * getter for the servletInstance property.
   * 
   * @return the servletInstance
   */
  public Servlet getServletInstance()
  {
    return servletInstance;
  }



  /**
   * setter for the servletInstance property.
   * 
   * @param servletInstance
   *          the servletInstance to set
   */
  public void setServletInstance(Servlet servletInstance)
  {
    this.servletInstance = servletInstance;
  }



  /**
   * setter for the servletName property.
   * 
   * @param servletName
   *          the servletName to set
   */
  public void setServletName(String servletName)
  {
    this.servletName = servletName;
  }



  /**
   * setter for the initParameters property.
   * 
   * @param initParameters
   *          the initParameters to set
   */
  public void setInitParameters(Properties initParameters)
  {
    this.initParameters = initParameters;
  }



  /**
   * setter for the beanName property.
   * 
   * @param beanName
   *          the beanName to set
   */
  public void setBeanName(String beanName)
  {
    this.beanName = beanName;
  }



  /**
   * getter for the servletPath property.
   * @return the servletPath
   */
  public String getServletPath()
  {
    return servletPath;
  }



  /**
   * setter for the servletPath property.
   * @param servletPath the servletPath to set
   */
  public void setServletPath(String servletPath)
  {
    this.servletPath = servletPath;
  }

}
